package com.bookworms.helloandengine.game.utils;

import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.entity.IEntity;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXLayer;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXObject;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXObjectGroup;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXTile;
import org.anddev.andengine.entity.layer.tiled.tmx.util.exception.TMXLoadException;
import org.anddev.andengine.entity.modifier.FadeInModifier;
import org.anddev.andengine.entity.modifier.FadeOutModifier;
import org.anddev.andengine.entity.modifier.IEntityModifier.IEntityModifierListener;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.util.Debug;
import org.anddev.andengine.util.modifier.IModifier;
import org.anddev.andengine.util.modifier.ease.EaseLinear;

import android.os.AsyncTask;
import com.bookworms.helloandengine.game.ui.GameScreen;
import com.bookworms.helloandengine.game.UI;

public class TMXMapUpdater extends GameScreen {
    public TMXMapUpdater() {
    }

    public static void doProperty1(TMXTile PlayerTile) {
        //This needs to be called in the update handler

        //Do this property only on the tiles that the player steps on
        if (propertyTiles.contains(PlayerTile)) {

            int tempArrayIndex = propertyTiles.indexOf(PlayerTile);
            int tempIndex = propertyIndex.get(tempArrayIndex);
            //I just did something random
            if (tempIndex == 1) {
                PlayerTile.setGlobalTileID(mTMXTiledMap, PlayerTile.getGlobalTileID() - 1);
            }
        }
    }

    //***********************************************************************************************************************

    public static void doTileAnimations() {
        //cycles through the array and changes the global values
        //Note: you need to know the number of the tiles you want to cycle through
        for (TMXTile temptile : animatedTiles) {

            //Controls the animation of the bush
            switch (temptile.getGlobalTileID()) {
                case 3:
                    temptile.setGlobalTileID(mTMXTiledMap, 19);
                    break;

                case 19:
                    temptile.setGlobalTileID(mTMXTiledMap, 35);
                    break;

                case 35:
                    temptile.setGlobalTileID(mTMXTiledMap, 3);
                    break;
                default:
                    break;
            }

            //Controls the animation of the rock
            switch (temptile.getGlobalTileID()) {
                case 145:
                    temptile.setGlobalTileID(mTMXTiledMap, 161);
                    break;

                case 161:
                    temptile.setGlobalTileID(mTMXTiledMap, 177);
                    break;

                case 177:
                    temptile.setGlobalTileID(mTMXTiledMap, 145);
                    break;
                default:
                    break;
            }

        }
    }

    //************************************************************************************************************************

    public static void fadeFromBlack(final float posX, final float posY) {

        mFadeScreen.setPosition(posX - mFadeScreen.getWidth() / 2, posY - mFadeScreen.getHeight() / 2);

        /************
         * THIS IS IMPORTANT************** The number indicates the Tiled layers
         * you have in your map.It needs to be higher than the amount of layers
         * you have on your scene. It can't be too high either otherwise the
         * screen will not respond. So far this solution works
         */

            mScene.attachChild(mFadeScreen, mScene.getChildCount());

        //Sets up the fade properties
        FadeOutModifier prFadeOutModifier = new FadeOutModifier(1f, new IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {

                /*
                     * This controls the walking animation after the scene has
                     * loaded The reason I used numbers is because switch statements
                     * do not support strings. If you look at your num pad I think
                     * it should make sense
                     */
                switch (playerSpawnDirection) {
                    case 4:
                        //LEFT
                        UI.walkToSelectedPoint(posX - TILE_WIDTH, posY, mScene);
                        break;

                    case 6:
                        //RIGHT
                        UI.walkToSelectedPoint(posX + TILE_WIDTH, posY, mScene);
                        break;

                    case 2:
                        //DOWN
                        UI.walkToSelectedPoint(posX, posY + TILE_HEIGHT, mScene);
                        break;

                    case 8:
                        //UP
                        UI.walkToSelectedPoint(posX, posY - TILE_HEIGHT, mScene);
                        break;

                    default:
                        break;
                }
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                //Once the scene has faded to the scene then the map has finished loading
                //See OnSceneTouchEvent and the updateHandler to see what this controls
                MapIsLoading = false;
            }
        }, EaseLinear.getInstance());

        //Apply the fade properties
        mFadeScreen.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
        mFadeScreen.registerEntityModifier(prFadeOutModifier);

    }

    //************************************************************************************************************************

    public static void fadeToBlack() {

        mFadeScreen.setPosition(playerLocationTile.getTileX() - mFadeScreen.getWidth() / 2, playerLocationTile.getTileY()
                - mFadeScreen.getHeight() / 2);

        //Sets up the fade properties
        FadeInModifier prFadeInModifier = new FadeInModifier(1f, new IEntityModifierListener() {
            @Override
            public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
            }

            @Override
            public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
                /*
                     * The first index is the position in the array the Exit tile,
                     * at the players feet, is located at.The second index is the
                     * map number associated with the first Index
                     */
                int tempTransIndex = ExitTiles.indexOf(playerLocationTile);
                int tempMapIndex = MapNumber.get(tempTransIndex);
                if (LinkNumber.get(tempTransIndex) != null)
                    mLinkIndex = LinkNumber.get(tempTransIndex);
                //loads & attaches the assets for the next map
                loadAndAttachAssets(tempMapIndex);

                mScene.detachChild(mFadeScreen);

            }
        }, EaseLinear.getInstance());

        //Apply the fade properties
        mFadeScreen.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
        mFadeScreen.registerEntityModifier(prFadeInModifier);

    }

    //*******************************************************************************************************************

    public static void getPropertyTiles() {
        propertyTiles = new ArrayList<TMXTile>();
        propertyIndex = new ArrayList<Integer>();
        // The for loop cycles through each object on the map
        for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

            // Gets the object layer with these properties. Use if you have many object layers, Otherwise this can be removed
            if (group.getTMXObjectGroupProperties().get(0).getName().equals("Property")) {

                for (final TMXObject object : group.getTMXObjects()) {
                    ObjectX = object.getX() + TILE_WIDTH / 2;
                    ObjectY = object.getY() + TILE_HEIGHT / 2;
                    // Gets the number of rows and columns in the object
                    ObjectHeight = object.getHeight() / TILE_HEIGHT;
                    ObjectWidth = object.getWidth() / TILE_WIDTH;

                    // Gets the tiles the object covers and puts it into the Arraylist mCollideTiles
                    for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
                        for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
                            TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
                                    + TileRow * TILE_HEIGHT);

                            propertyTiles.add(tempTile);
                            propertyIndex.add(Integer.parseInt(group.getTMXObjectGroupProperties().get(0).getValue()));
                        }
                    }

                }
            }

        }
    }

    //*********************************************************************************************************************

    public static void getAnimatedTiles() {
        animatedTiles = new ArrayList<TMXTile>();

        for (int TileRow = 0; TileRow < mAnimateLayer.getTileRows(); TileRow++) {
            for (int TileColumn = 0; TileColumn < mAnimateLayer.getTileColumns(); TileColumn++) {
                TMXTile tempTile = mAnimateLayer.getTMXTileAt(TILE_WIDTH / 2 + TileColumn * TILE_WIDTH, TILE_HEIGHT / 2
                        + TileRow * TILE_HEIGHT);
                if (tempTile != null)
                    animatedTiles.add(tempTile);
            }
        }
    }

    //*********************************************************************************************************************

    public static void getCollideTiles() {
        /*
           * Determines the tiles that have the Object layer
           */
        // Uses an array list because the size will need to be
        // changed dynamically.
        mCollideTiles = new ArrayList<TMXTile>();
        // The for loop cycles through each object on the map
        for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

            // Gets the object layer with these properties. Use if
            // you have many object layers, Otherwise this can be
            // removed
            if (group.getTMXObjectGroupProperties().containsTMXProperty("Collide", "true")) {

                for (final TMXObject object : group.getTMXObjects()) {

                    ObjectX = object.getX() + TILE_WIDTH / 2;
                    ObjectY = object.getY() + TILE_HEIGHT / 2;
                    // Gets the number of rows and columns in the
                    // object
                    ObjectHeight = object.getHeight() / TILE_HEIGHT;
                    ObjectWidth = object.getWidth() / TILE_WIDTH;

                    // Gets the tiles the object covers and puts it
                    // into the Arraylist mCollideTiles
                    for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
                        for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
                            TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
                                    + TileRow * TILE_HEIGHT);
                            mCollideTiles.add(tempTile);
                        }
                    }

                }
            }

        }
    }

    //*********************************************************************************************************************

    public static void getPlayerSpawnTiles() {
        playerSpawnTiles = new ArrayList<TMXTile>();

        // The for loop cycles through each object on the map
        for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

            // Gets the object layer with these properties. Use if you have many object layers, Otherwise this can be removed
            if (group.getTMXObjectGroupProperties().containsTMXProperty("Exit", Integer.toString(mCurrentMapIndex))) {

                for (final TMXObject object : group.getTMXObjects()) {
                    ObjectX = object.getX() + TILE_WIDTH / 2;
                    ObjectY = object.getY() + TILE_HEIGHT / 2;
                    // Gets the number of rows and columns in the
                    // object
                    ObjectHeight = object.getHeight() / TILE_HEIGHT;
                    ObjectWidth = object.getWidth() / TILE_WIDTH;

                    // Gets the tiles the object covers and puts it into the Arraylist mCollideTiles
                    for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
                        for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
                            TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
                                    + TileRow * TILE_HEIGHT);

                            //If the tile has a specific link then go to the specific location else go to the only location
                            if (group.getTMXObjectGroupProperties().containsTMXProperty("Link",
                                    Integer.toString(mLinkIndex))) {

                                playerSpawnTiles.add(tempTile);
                                //The second property needs to be the player direction he will be walking in when he enters the map
                                playerSpawnDirection = Integer.parseInt(group.getTMXObjectGroupProperties().get(2)
                                        .getValue());

                            } else if (group.getTMXObjectGroupProperties().size() < 3) {
                                playerSpawnTiles.add(tempTile);
                                //This works if there are only two properties
                                playerSpawnDirection = Integer.parseInt(group.getTMXObjectGroupProperties().get(1)
                                        .getValue());

                            }

                        }
                    }

                }
            }

        }
    }

    //*********************************************************************************************************************

    public static void getExitTiles() {
        // Uses an array list because the size will need to be changed dynamically.
        ExitTiles = new ArrayList<TMXTile>();
        MapNumber = new ArrayList<Integer>();
        LinkNumber = new ArrayList<Integer>();
        // The for loop cycles through each object on the map
        for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

            /*
                * Gets the object layer with these properties. Use if you have many
                * object layers, Otherwise this can be removed
                */
            if (group.getTMXObjectGroupProperties().get(0).getName().equals("Exit")) {

                for (final TMXObject object : group.getTMXObjects()) {

                    ObjectX = object.getX() + TILE_WIDTH / 2;
                    ObjectY = object.getY() + TILE_HEIGHT / 2;
                    // Gets the number of rows and columns in the object
                    ObjectHeight = object.getHeight() / TILE_HEIGHT;
                    ObjectWidth = object.getWidth() / TILE_WIDTH;

                    // Gets the tiles the object covers and puts it into the Arraylist mCollideTiles
                    for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
                        for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
                            TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
                                    + TileRow * TILE_HEIGHT);
                            ExitTiles.add(tempTile);
                            MapNumber.add(Integer.parseInt(group.getTMXObjectGroupProperties().get(0).getValue()));

                            //This means that the group has the link property in it
                            if (group.getTMXObjectGroupProperties().size() >= 3) {
                                LinkNumber.add(Integer.parseInt(group.getTMXObjectGroupProperties().get(1).getValue()));
                                /*
                                         * you need to know the order of your
                                         * properties. Use the following if you are not
                                         * sure, and check logCat:
                                         * Log.i("Link", group.getTMXObjectGroupProperties().get(1).getValue());
                                         */
                            } else {
                                //fills the array to match ExitTiles
                                LinkNumber.add(null);
                            }
                        }
                    }

                }
            }

        }
    }

    //*********************************************************************************************************************

    public static void updateTMXMap() {

        if (ExitTiles.contains(playerLocationTile) && playerLocationTile.getTileX() % 32 == 0
                && playerLocationTile.getTileY() % 32 == 0) {

            /*
                * if on the exit tile, the player won't be able to interact with
                * the screen and the update will stop until the tmx has loaded.
                * Once the load has finished the player will appear on the new map
                * off of the exit tile and the MapIsLoading variable will return
                * false
                */
            MapIsLoading = true;
            UpdateTiles = false;
            //Ends the path at the tile the sprite is on
            UI.walkToSelectedPoint(playerLocationTile.getTileX(), playerLocationTile.getTileY(), mScene);

            //Contains the TMXMap loader in onModifierFinished
            fadeToBlack();

        }
    }

    //********************************************************************************************************************

    public static void getTMXMap(int MapIndex) {
        //If the index stored from the large map equals the map index being called than use the map that has been saved
        //This cuts the loading time down for large maps but increases the amount of memory used
        if (mLargeTMXIndex == MapIndex) {
            mTMXTiledMap = mLargeTMXMap;
        } else {

            switch (MapIndex) {
                case 0:
                    // This is the initial map the player spawns on
                    try {
                        mTMXTiledMap = mTmxLoader.loadFromAsset(mContext, "tmx/test.tmx");
                    } catch (final TMXLoadException tmxle) {
                        Debug.e(tmxle);
                    }

                    break;

                case 1:
                    //This is the large 200x200 tiled map. It usually takes 8 to 30 seconds to load depending on the device
                    try {
                        mTMXTiledMap = mTmxLoader.loadFromAsset(mContext, "tmx/Route.tmx");
                        //Put this in any large TMX you want to keep in memory. This will store the most recent one
                        mLargeTMXIndex = MapIndex;
                        //Save the recently loaded large map in memory
                        mLargeTMXMap = mTMXTiledMap;
                    } catch (final TMXLoadException tmxle) {
                        Debug.e(tmxle);
                    }
                    break;

                case 2:
                    //This is the first level of the building
                    try {
                        mTMXTiledMap = mTmxLoader.loadFromAsset(mContext, "tmx/Building1.tmx");
                    } catch (final TMXLoadException tmxle) {
                        Debug.e(tmxle);
                    }
                    break;

                case 3:
                    // this is the second floor of the building
                    try {
                        mTMXTiledMap = mTmxLoader.loadFromAsset(mContext, "tmx/Building2.tmx");
                    } catch (final TMXLoadException tmxle) {
                        Debug.e(tmxle);
                    }
                    break;
                default:

                    break;
            }
        }
    }

    //*********************************************************************************************************************

    public static void loadAndAttachAssets(final int MapIndex) {

        //Use AsyncTask to load TMX map from a saved state
        new AsyncTask<Void, Integer, Void>() {

            @Override
            protected void onPreExecute() {
                //Clears the scene for the next map
                super.onPreExecute();
                mScene.clearEntityModifiers();
                mScene.detachChildren();
                walkingPath = null;
                //Clears all layers of their values
                mAnimateLayer = null;
                mTopLayer = null;
            }

            //Set what gets done in the background
            @Override
            protected Void doInBackground(Void... params) {

                /*
                     * MapIsLoading is needed here because the initial load can be
                     * anywhere not just on an exit tile. SEE updateTMXMap() method
                     */
                MapIsLoading = true;

                // Load the TMX map
                getTMXMap(MapIndex);

                AssetsAttached = false;

                return null;
            }

            //Set what gets done after doInBackgroung ends
            @Override
            protected void onPostExecute(Void result) {
                System.gc();
                AttachAssets();

            }

        }.execute();
    }

    //**********************************************************************************************************************

    public static void AttachAssets() {
        /*
           * Attaches the layers for this map and sets them to be invisible.
           * Explained below.
           */
        if (!AssetsAttached) {
            for (int LayerIndex = 0; LayerIndex < mTMXTiledMap.getTMXLayers().size(); LayerIndex++) {
                TMXLayer layer = mTMXTiledMap.getTMXLayers().get(LayerIndex);
                if (layer.getTMXLayerProperties().containsTMXProperty("Top", "true")) {
                    //This layer is attached later
                    layer.setVisible(false);
                    mTopLayer = layer;
                }
                if (layer.getTMXLayerProperties().containsTMXProperty("Animate", "true")) {
                    mAnimateLayer = layer;
                }
                if (!layer.getTMXLayerProperties().containsTMXProperty("Top", "true")) {
                    layer.setVisible(false);
                    mScene.attachChild(layer);
                }
            }

            // Layer initialization
            TMXMapLayer = mTMXTiledMap.getTMXLayers().get(0);

            //Gets the tiles which activate special events
            getExitTiles();
            getPlayerSpawnTiles();
            getPropertyTiles();
            getCollideTiles();
            if (mAnimateLayer != null) {
                getAnimatedTiles();
            }

            //Gets the spawn location of the player sprite
            float playerX;
            float playerY;
            if (playerSpawnTiles.size() > 0) {
                playerX = playerSpawnTiles.get(0).getTileX();
                playerY = playerSpawnTiles.get(0).getTileY();

            } else {
                playerX = InitialplayerSpawnX;
                playerY = InitialplayerSpawnY;

            }

            // Add player sprite to the scene with the chase camera on the player sprite
            // Create the player sprite at the specified location
            player = new AnimatedSprite(playerX, playerY, mPlayerTextureRegion);
            player.setVisible(false);
            mScene.attachChild(player);

            //Attach the top layer, if it is on the map, after the player is added so that the player can travel under it
            if (mTopLayer != null)
                mScene.attachChild(mTopLayer);

            /*
                * This activates the ability to Update the player location on the
                * tiles. It is used in the UpdateHandler. This enables the walking
                * effect when a scene changes, since the walkto method requires the
                * players current tile to make a path
                */
            UpdateTiles = true;

            // Make the camera not exceed the bounds of the TMXEntity. Sets the Chase entity and Sets the center of the camera
            mBoundChaseCamera.setCenter(playerX, playerY);
            mBoundChaseCamera.setChaseEntity(player);
            mBoundChaseCamera.setBounds(0, TMXMapLayer.getWidth(), 0, TMXMapLayer.getHeight());
            mBoundChaseCamera.setBoundsEnabled(EnableBounds);

            //Depending on the option settings the Dpad or touch will be enabled
            UI.GeneralUI();

            //Only attaches the assets once
            AssetsAttached = true;

            /*
                * Make all layers visible right before fading in. If you know the
                * layers you are using then it may be easier to just add them
                * manuallyThis is used to prevent the flickering caused by the
                * moving camera. This also ensures a smooth transition since
                * everything has been loaded
                */
            for (int LayerIndex = 0; LayerIndex < mTMXTiledMap.getTMXLayers().size(); LayerIndex++) {
                TMXLayer layer = mTMXTiledMap.getTMXLayers().get(LayerIndex);
                layer.setVisible(true);

            }
            player.setVisible(true);

            //Calls the fade method. See above for method details.
            fadeFromBlack(playerX, playerY);

            //Removes the detached layers
            System.gc();
        }

    }


}
	




